home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Ham Radio 2000 #2
/
Ham Radio 2000 - Volume 2.iso
/
HAMV2
/
MISC
/
DTMFF110
/
SBIO.C
< prev
next >
Wrap
C/C++ Source or Header
|
1997-08-05
|
14KB
|
569 lines
/* SBIO.C */
/* Soundblaster 16 basic audio I/O functions */
/* Copyright 1995 by Ethan Brodsky. All rights reserved */
/* Modified extensively by Philip VanBaren to suit my purposes */
/* Modified by Emil Laurentiu Wednesday, 06 August 1997 */
/* Interface variables that can be changed in the background */
volatile int sb16dmarunning;
volatile char curblock;
#include <alloc.h>
#include <stdio.h>
#include <dos.h>
#include <mem.h>
#include <stdlib.h>
#include "sbio.h"
#include "freq.h" /* Needed for DOUT() definition only */
#define lo(value) (unsigned char)((value) & 0x00FF)
#define hi(value) (unsigned char)((value) >> 8)
int mixerport;
int mixdataport;
int resetport;
int readport;
int writeport;
int pollport;
int poll16port;
int pic_rotateport;
int pic_maskport;
int dma_maskport;
int dma_clrptrport;
int dma_modeport;
int dma_baseaddrport;
int dma_countport;
int dma_pageport;
char irq_startmask;
char irq_stopmask;
char irq_intvector;
char int_controller;
char dma_startmask;
char dma_stopmask;
char dma_mode;
/* This function is defined in sc_sb16.c */
extern void interrupt sb16_callback( void );
void
interrupt( *oldintvector ) ( ) = NULL;
int handlerinstalled;
void far *dmabuffer = NULL; /* Twice the size of the output buffer */
int far *dmaptr = NULL; /* Pointer to the used portion */
unsigned long buf_addr; /* 16-bit addressing */
unsigned char buf_page;
unsigned int buf_ofs;
mode iomode; /* Flags input or output mode */
/* Low level sound card I/O */
void
write_dsp( unsigned char value )
{
while ( inp( writeport ) & 0x80 ); /* Wait for bit 7 to be cleared */
outp( writeport, value );
}
unsigned char
read_dsp( void )
{
long timeout = 1000000L;
/* Wait for bit 7 to be set, or for a timeout */
while ( ( !( inp( pollport ) & 0x80 ) ) && ( --timeout ) );
return inp( readport );
}
int
reset_dsp( void )
{
int i;
sb16dmarunning = 0;
outp( resetport, 1 );
outp( resetport, 0 );
i = 100;
while ( ( read_dsp( ) != 0xAA ) && i-- );
return i;
}
/* Convert a far pointer to a linear address, for DMA addressing */
#define getlinearaddr(p) ((unsigned long)FP_SEG(p)*16 + (unsigned long)FP_OFF(p))
/* Initialization and shutdown */
void
installhandler( void )
{
/* Install the interrupt handler */
disable( ); /* Disable interrupts */
outp( pic_maskport, ( inp( pic_maskport ) | irq_stopmask ) ); /* Mask IRQ */
oldintvector = getvect( irq_intvector ); /* Save old vector */
setvect( irq_intvector, sb16_callback ); /* Install new handler */
outp( pic_maskport, ( inp( pic_maskport ) & irq_startmask ) ); /* Unmask IRQ */
enable( ); /* Reenable interupts */
handlerinstalled = 1;
}
void
uninstallhandler( void )
{
sb16dmarunning = 0;
disable( ); /* Disable interrupts */
outp( pic_maskport, ( inp( pic_maskport ) | irq_stopmask ) ); /* Mask IRQ */
setvect( irq_intvector, oldintvector ); /* Restore old vector */
enable( ); /* Enable interrupts */
handlerinstalled = 0;
}
/* This function is run at program exit to ensure cleanup */
void
sb_exitproc( void )
{
sb16dmarunning = 0;
outp( 0x20, 0x20 );
outp( 0xA0, 0x20 ); /* Acknowledge any hanging ints */
write_dsp( 0xD5 ); /* Stop digitized sound xfer */
outp( dma_maskport, dma_stopmask ); /* Mask DMA channel */
if ( handlerinstalled )
uninstallhandler( ); /* Uninstall int handler */
reset_dsp( ); /* Reset SB DSP */
}
/*
* Initialize the Soundblaster-16 card with the settings:
* baseio: base IO address for the card
* irq: IRQ channel used by the card
* dma16: 16-bit DMA channel used by the card
* io: sampling mode, either "input" or "output"
* length: maximum block size to be used, in samples
* (this is used for allocating the DMA buffer)
*/
int
init_sb( int baseio, char irq, char dma16, mode io, unsigned int length )
{
int val, onboard;
/* Sound card IO ports */
mixerport = baseio + 0x004;
mixdataport = baseio + 0x005;
resetport = baseio + 0x006;
readport = baseio + 0x00A;
writeport = baseio + 0x00C;
pollport = baseio + 0x00E;
poll16port = baseio + 0x00F;
/* Reset DSP */
sb16dmarunning = 0;
if ( !reset_dsp( ) )
return 0;
/* Verify that we have a SB-16 at this address */
write_dsp( 0xE1 );
val = read_dsp( ); /* Get the major version number */
read_dsp( ); /* Grab the minor version number also */
if ( val < 4 )
return 0;
/*
* Check the current IRQ and DMA settings, and compare with the values
* passed to this routine
*/
outportb( mixerport, 0x80 ); /* IRQ settings */
val = inportb( mixdataport );
onboard = -1;
if ( val & 0x01 )
{
onboard = 2;
DOUT( "SB16: IRQ2 enabled" );
}
if ( val & 0x02 )
{
onboard = 5;
DOUT( "SB16: IRQ5 enabled" );
}
if ( val & 0x04 )
{
onboard = 7;
DOUT( "SB16: IRQ7 enabled" );
}
if ( val & 0x08 )
{
onboard = 10;
DOUT( "SB16: IRQ10 enabled" );
}
if ( onboard == -1 )
{
puts( "Error: Soundblaster has no IRQ enabled, aborting ..." );
return ( 0 );
}
switch ( irq )
{
case 2:
if ( !( val & 0x01 ) )
{
irq = -1;
} break;
case 5:
if ( !( val & 0x02 ) )
{
irq = -1;
} break;
case 7:
if ( !( val & 0x04 ) )
{
irq = -1;
} break;
case 10:
if ( !( val & 0x08 ) )
{
irq = -1;
} break;
default:
irq = -1;
}
if ( irq == -1 )
{
printf( "Warning: Soundblaster has IRQ%d selected, using that value instead\n", onboard );
irq = onboard;
}
outportb( mixerport, 0x81 ); /* DMA settings */
val = inportb( mixdataport );
onboard = -1;
if ( val & 0x01 )
{ /* onboard=0; */
DOUT( "SB16: DMA0 enabled" );
}
if ( val & 0x02 )
{ /* onboard=1; */
DOUT( "SB16: DMA1 enabled" );
}
if ( val & 0x08 )
{ /* onboard=3; */
DOUT( "SB16: DMA3 enabled" );
}
if ( val & 0x20 )
{
onboard = 5;
DOUT( "SB16: DMA5 enabled" );
}
if ( val & 0x40 )
{
onboard = 6;
DOUT( "SB16: DMA6 enabled" );
}
if ( val & 0x80 )
{
onboard = 7;
DOUT( "SB16: DMA7 enabled" );
}
if ( onboard == -1 )
{
puts( "Error: Soundblaster has no 16-bit DMA enabled, aborting ..." );
return ( 0 );
}
switch ( dma16 )
{
// case 0: if(!(val&0x01)) { dma16=-1; } break;
// case 1: if(!(val&0x02)) { dma16=-1; } break;
// case 3: if(!(val&0x08)) { dma16=-1; } break;
case 5:
if ( !( val & 0x20 ) )
{
dma16 = -1;
} break;
case 6:
if ( !( val & 0x40 ) )
{
dma16 = -1;
} break;
case 7:
if ( !( val & 0x80 ) )
{
dma16 = -1;
} break;
default:
dma16 = -1;
}
if ( dma16 == -1 )
{
printf( "Warning: Soundblaster has DMA%d selected, using that value instead\n", onboard );
dma16 = onboard;
}
#ifdef DEBUG_OUTPUT
/* Check if the IRQs are enabled */
outportb( mixerport, 0x82 ); /* IRQ status */
val = inportb( mixdataport );
if ( val & 0x01 )
{
DOUT( "SB16: 8-bit IRQ active" );
}
if ( val & 0x02 )
{
DOUT( "SB16: 16-bit IRQ active" );
}
if ( val & 0x04 )
{
DOUT( "SB16: MPU-401 IRQ active" );
}
#endif
/* These two lines are strictly a kludge for freq.exe */
outportb( mixerport, 0x3d ); /* Input control */
outportb( mixdataport, 0x7f );/* Use all channels for input */
/* Compute interrupt ports and parameters */
if ( irq < 8 )
{
int_controller = 1;
pic_rotateport = 0x20;
pic_maskport = 0x21;
irq_intvector = 0x08 + irq;
}
else
{
int_controller = 2;
pic_rotateport = 0xA0;
pic_maskport = 0x21;
irq_intvector = 0x70 + irq - 8;
}
irq_stopmask = 1 << ( irq % 8 );
irq_startmask = ~irq_stopmask;
/* Compute DMA ports and parameters */
dma_maskport = 0xD4;
dma_clrptrport = 0xD8;
dma_modeport = 0xD6;
dma_baseaddrport = 0xC0 + 4 * ( dma16 - 4 );
dma_countport = 0xC2 + 4 * ( dma16 - 4 );
switch ( dma16 )
{
case 5:
dma_pageport = 0x8B;
break;
case 6:
dma_pageport = 0x89;
break;
case 7:
dma_pageport = 0x8A;
break;
}
dma_stopmask = dma16 - 4 + 0x04; /* 000001xx */
dma_startmask = dma16 - 4 + 0x00; /* 000000xx */
/* Allocate a buffer for DMA transfer */
/* (need a block of memory that does not cross a page boundary) */
if ( ( dmabuffer = farmalloc( 8 * length ) ) == NULL )
{
puts( "Unable to allocate DMA buffer." );
exit( 1 );
}
dmaptr = ( int far * ) dmabuffer;
if ( ( ( getlinearaddr( dmabuffer ) >> 1 ) % 65536L ) + length * 2 > 65536L )
dmaptr += 2 * length; /* Pick second half to avoid crossing
* boundary */
/* Compute DMA parameters */
buf_addr = getlinearaddr( dmaptr );
buf_page = ( unsigned int ) ( buf_addr >> 16 );
buf_ofs = ( unsigned int ) ( ( buf_addr >> 1 ) % 65536L );
/* Other initialization */
iomode = io;
switch ( iomode )
{
case input:
dma_mode = dma16 - 4 + 0x54;
break; /* 010101xx */
case output:
dma_mode = dma16 - 4 + 0x58;
break; /* 010110xx */
}
installhandler( ); /* Install interrupt handler */
atexit( sb_exitproc ); /* Install exit procedure */
return 1;
}
/*
* Shut down the sampling process, clean up the interrupts,
* and free up the memory allocation
*/
void
shutdown_sb( void )
{
sb16dmarunning = 0;
reset_dsp( );
if ( handlerinstalled )
uninstallhandler( );
farfree( dmabuffer );
}
/*
* Start continuous I/O at a rate of {rate} Hz, with a call to the
* callback_sb16 procedure after every block of length {length} has
* been finished. The sampling input or output is done on alternating
* blocks pointed to by (dmaptr+curblock*length).
* i.e. When curblock is 0, the current block is (dmaptr).
* When curblock is 1, the current block is (dmaptr+length)
* The variable curblock is maintained in the callback_sb16 routine.
*/
void
startio( unsigned int rate, unsigned long length )
{
sb16dmarunning = 1;
curblock = 0;
/* Program DMA controller */
outp( dma_maskport, dma_stopmask );
outp( dma_clrptrport, 0x00 );
outp( dma_modeport, dma_mode );
outp( dma_baseaddrport, lo( buf_ofs ) ); /* Low byte of offset */
outp( dma_baseaddrport, hi( buf_ofs ) ); /* High word of offset */
outp( dma_countport, lo( length * 2 - 1 ) ); /* Low byte of count */
outp( dma_countport, hi( length * 2 - 1 ) ); /* High byte of count */
outp( dma_pageport, buf_page );
outp( dma_maskport, dma_startmask );
/* Program sound card */
switch ( iomode )
{
case input:
write_dsp( 0x42 );
break; /* Set input sampling rate */
case output:
write_dsp( 0x41 );
break; /* Set output sampling rate */
}
write_dsp( hi( rate ) ); /* High byte of sampling rate */
write_dsp( lo( rate ) ); /* Low byte of sampling rate */
switch ( iomode )
{
case output:
write_dsp( 0xB6 );
break; /* 16-bit D->A, A/I, FIFO */
case input:
write_dsp( 0xBE );
break; /* 16-bit A->D, A/I, FIFO */
}
write_dsp( 0x10 ); /* DMA Mode: 16-bit signed mono */
write_dsp( lo( length - 1 ) );/* Low byte of block length */
write_dsp( hi( length - 1 ) );/* High byte of block length */
}
/*
* Stops the current sampling process.
*/
void
stopio( void )
{
outp( 0x20, 0x20 );
outp( 0xA0, 0x20 ); /* Acknowledge any hanging ints */
write_dsp( 0xD9 ); /* Stop digitized sound xfer */
outp( dma_maskport, dma_stopmask ); /* Mask DMA channel */
sb16dmarunning = 0;
reset_dsp( ); /* Reset SB DSP */
}
/* Mixer setting and reading functions */
void
set_cd_level( unsigned int level )
{
level = level * 256 / 100;
if ( level > 255 )
level = 255;
outportb( mixerport, 0x36 ); /* CD Audio left */
outportb( mixdataport, level );
outportb( mixerport, 0x37 ); /* CD Audio right */
outportb( mixdataport, level );
}
void
set_mic_level( unsigned int level )
{
level = level * 256 / 100;
if ( level > 255 )
level = 255;
outportb( mixerport, 0x3A ); /* Microphone */
outportb( mixdataport, level );
}
void
set_line_level( unsigned int level )
{
level = level * 256 / 100;
if ( level > 255 )
level = 255;
outportb( mixerport, 0x38 ); /* Line In left */
outportb( mixdataport, level );
outportb( mixerport, 0x39 ); /* Line In right */
outportb( mixdataport, level );
}
unsigned int
get_cd_level( void )
{
unsigned int level;
outportb( mixerport, 0x36 ); /* CD Audio left */
level = inportb( mixdataport );
outportb( mixerport, 0x37 ); /* CD Audio right */
level += inportb( mixdataport );
level = level * 50 / 256;
if ( level > 100 )
level = 100;
return ( level );
}
unsigned int
get_mic_level( void )
{
unsigned int level;
outportb( mixerport, 0x3A ); /* Microphone */
level = inportb( mixdataport );
level = level * 100 / 256;
if ( level > 100 )
level = 100;
return ( level );
}
unsigned int
get_line_level( void )
{
unsigned int level;
outportb( mixerport, 0x38 ); /* Line In left */
level = inportb( mixdataport );
outportb( mixerport, 0x39 ); /* Line In right */
level += inportb( mixdataport );
level = level * 50 / 256;
if ( level > 100 )
level = 100;
return ( level );
}
void
set_master_level( int level )
{
level &= 0xf;
level |= level << 4;
outportb( sb_addr + 0x4, 0x22 ); /* Master volume */
outportb( sb_addr + 0x5, level );
return;
}
void
set_fm_level( int level )
{
level &= 0xf;
level |= level << 4;
outportb( sb_addr + 0x4, 0x26 ); /* FM volume */
outportb( sb_addr + 0x5, level );
return;
}